/*
 **************************************************************************
 *                                                                        *
 *                     Binary Data Serializer Library                     *
 *                                                                        *
 * Author: Jakub Szumacher (2013)                                	    *
 *                                                                        *
 **************************************************************************
*/

/*
 * Caution:
 *
 * - no support: STL containers (except vector), pointers, arrays
 *
 */

#ifndef INCLUDE_BINARYSERIALIZER
#define INCLUDE_BINARYSERIALIZER

#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <vector>

using namespace std;

/*****************************************************************************************************
* BinarySerializerStream
******************************************************************************************************/
class BinarySerializerStream
{
public:
  ///Destructor
  virtual ~BinarySerializerStream() {};

  ///Is out stream info
  virtual bool IsOutStream() = 0;
};

/*****************************************************************************************************
* Classes
******************************************************************************************************/

class BinarySerializerStreamIn;
class BinarySerializerStreamOut;

/*****************************************************************************************************
* BinarySerializerAccess
******************************************************************************************************/
class BinarySerializerAccess
{
public:
  template<class T>
  static void Serialize(BinarySerializerStreamIn& binaryStream, T& inputClass)
  {
    inputClass.Serialize(binaryStream);
  };
  template<class T>
  static void Deserialize(BinarySerializerStreamOut& binaryStream, T& inputClass)
  {
    inputClass.Serialize(binaryStream);
  };
  template<class T>
  static void SplitMembers(BinarySerializerStream* binaryStream, T& inputClass)
  {
    if(binaryStream->IsOutStream())
      inputClass.Load(*(BinarySerializerStreamOut*)binaryStream);
    else
      inputClass.Save(*(BinarySerializerStreamIn*)binaryStream);
  };
};

/*****************************************************************************************************
* BinarySerializerStreamIn
******************************************************************************************************/
class BinarySerializerStreamIn : public BinarySerializerStream
{
private:

  #define resizeBufferStep 128;

  unsigned char* buffer;
  int bufferCapacity;
  int bufferSize;

  void ResizeBuffer(int addLen)
  {
    //Not allocated!
    if(!buffer)
    {
      bufferCapacity = resizeBufferStep;
      buffer = new unsigned char[bufferCapacity];
    }

    //Need resize
    if((bufferSize + addLen) > bufferCapacity)
    {
      int newCapacity = bufferCapacity + resizeBufferStep;

      while((bufferSize + addLen) > newCapacity)
        newCapacity += resizeBufferStep;

      unsigned char* newBuffer = new unsigned char[newCapacity];

      memcpy(newBuffer, buffer, bufferSize);

      delete[] buffer;

      buffer = newBuffer;
      bufferCapacity = newCapacity;
    }
  };

  template<typename T>
  void SerializeType(const T& inputType)
  {
    ResizeBuffer(sizeof(T));
    memcpy(buffer + bufferSize, &inputType, sizeof(T));
    bufferSize += sizeof(T);
  };

  void SerializeString(const string& input)
  {
    unsigned long strLen = input.size();
    ResizeBuffer(sizeof(strLen) + strLen);
    memcpy(buffer + bufferSize, &strLen, sizeof(strLen));
    bufferSize += sizeof(strLen);
    memcpy(buffer + bufferSize, input.data(), strLen);
    bufferSize += strLen;
  };

  template<typename T>
  void SerializeVector(vector<T>& input)
  {
    unsigned long vectLen = input.size();
    ResizeBuffer(sizeof(vectLen));
    memcpy(buffer + bufferSize, &vectLen, sizeof(vectLen));
    bufferSize += sizeof(vectLen);
    for(unsigned long i = 0; i < input.size(); ++i)
    {
      *this & input[i];
    }
  };

  //Copy constructor - prohibit
  BinarySerializerStreamIn(const BinarySerializerStreamIn& c);

public:

  ///Constructor
  BinarySerializerStreamIn() : buffer(0), bufferSize(0), bufferCapacity(0) {};
  ///Destructor
  virtual ~BinarySerializerStreamIn()
  {
    if(buffer)
      delete[] buffer;
  };

  template<typename T>
  void operator & (T& input) { BinarySerializerAccess::Serialize(*this, input); };

  template<typename T>
  void operator & (vector<T>& input) { SerializeVector(input); };

  void operator & (char& input) { SerializeType(input); };
  void operator & (unsigned char& input) { SerializeType(input); };
  void operator & (short& input) { SerializeType(input); };
  void operator & (unsigned short& input) { SerializeType(input); };
  void operator & (long& input) { SerializeType(input); };
  void operator & (unsigned long& input) { SerializeType(input); };
  void operator & (int& input) { SerializeType(input); };
  void operator & (unsigned int& input) { SerializeType(input); };
  void operator & (float& input) { SerializeType(input); };
  void operator & (double& input) { SerializeType(input); };
  void operator & (bool& input) { SerializeType(input); };
  void operator & (string& input) { SerializeString(input); };

  const unsigned char* GetData() const { return buffer; };
  int GetSize() const { return bufferSize; };
  void ClearData() { bufferSize = 0; };

  bool IsOutStream() { return false; };
};

/*****************************************************************************************************
* BinarySerializerStreamOut
******************************************************************************************************/
class BinarySerializerStreamOut : public BinarySerializerStream
{
private:
  const unsigned char* buffer;
  int bufferSize;
  int bufferPos;

  template<typename T>
  void DeserializeType(T& outputType)
  {
    memcpy(&outputType, (buffer + bufferPos), sizeof(T));
    bufferPos += sizeof(T);
  };

  void DeserializeString(string& output)
  {
    output = "";
    unsigned long strLen = 0;
    memcpy(&strLen, (buffer + bufferPos), sizeof(strLen));
    bufferPos += sizeof(strLen);
    output.insert(0, (char *)(buffer + bufferPos), strLen);
    bufferPos += strLen;
  };

  template<typename T>
  void DeserializeVector(vector<T>& output)
  {
    output.clear();
    unsigned long vectLen = 0;
    memcpy(&vectLen, (buffer + bufferPos), sizeof(vectLen));
    bufferPos += sizeof(vectLen);
    for(unsigned long i = 0; i < vectLen; ++i)
    {
      T field;
      *this & field;
      output.push_back(field);
    }
  };

public:

  ///Constructor
  BinarySerializerStreamOut() : buffer(0), bufferSize(0), bufferPos(0) {};
  ///Destructor
  virtual ~BinarySerializerStreamOut() {};

  template<typename T>
  void operator & (T& output) { BinarySerializerAccess::Deserialize(*this, output); };

  template<typename T>
  void operator & (vector<T>& output) { DeserializeVector(output); };

  void operator & (char& output) { DeserializeType(output); };
  void operator & (unsigned char& output) { DeserializeType(output); };
  void operator & (short& output) { DeserializeType(output); };
  void operator & (unsigned short& output) { DeserializeType(output); };
  void operator & (long& output) { DeserializeType(output); };
  void operator & (unsigned long& output) { DeserializeType(output); };
  void operator & (int& output) { DeserializeType(output); };
  void operator & (unsigned int& output) { DeserializeType(output); };
  void operator & (float& output) { DeserializeType(output); };
  void operator & (double& output) { DeserializeType(output); };
  void operator & (bool& output) { DeserializeType(output); };
  void operator & (string& output) { DeserializeString(output); };

  void SetData(const unsigned char* buffer) { this->buffer = buffer; };
  void SetSize(int bufferSize) { this->bufferSize = bufferSize; };
  void ClearDataPos() { this->bufferPos = 0; };

  bool IsOutStream() { return true; };
};

/*****************************************************************************************************
* BinarySerializer
******************************************************************************************************/
class BinarySerializer
{
private:
  BinarySerializerStreamIn instream;

public:

  ///Constructor
  BinarySerializer() {};
  ///Destructor
  virtual ~BinarySerializer() {};

  template<class T>
  void operator << (const T& inputClass) { /*instream.ClearData();*/ BinarySerializerAccess::Serialize(instream, const_cast<T&>(inputClass)); };

  const unsigned char* GetData() const { return instream.GetData(); };
  int GetSize() const { return instream.GetSize(); };
};

/*****************************************************************************************************
* BinaryDeserializer
******************************************************************************************************/
class BinaryDeserializer
{
private:
  BinarySerializerStreamOut outstream;

public:

  ///Constructor
  BinaryDeserializer(const unsigned char* buffer, int bufferSize)
  {
    outstream.SetData(buffer);
    outstream.SetSize(bufferSize);
  };
  ///Destructor
  virtual ~BinaryDeserializer() {};

  template<class T>
  void operator >> (T& outputClass) { /*outstream.ClearDataPos();*/ BinarySerializerAccess::Deserialize(outstream, outputClass); };
};

#define BINARYSERIALIZER_SPLIT_MEMBERS()\
template<class Serializer>\
void Serialize(Serializer& s)\
{\
  BinarySerializerAccess::SplitMembers(&s, *this);\
};\

#endif

/*****************************************************************************************************
* EOF
******************************************************************************************************/



